iconcache: Ensure we don't lose data on power loss
authorColin Walters <walters@verbum.org>
Mon, 22 Nov 2010 19:42:50 +0000 (20:42 +0100)
committerJavier Jardón <jjardon@gnome.org>
Mon, 22 Nov 2010 19:49:59 +0000 (20:49 +0100)
fsync() should ensure our data hits disk; since corrupt icon
caches break all apps, we need to ensure it's valid.

Fixes https://bugzilla.gnome.org/show_bug.cgi?id=635307

gtk/updateiconcache.c

index 3b927011cccccc142363f35df8883ab0b1966dd9..7697a2c3f473c3fd6c9b1c35aabcdf479b954543 100644 (file)
@@ -1424,6 +1424,30 @@ validate_file (const gchar *file)
   return TRUE;
 }
 
+/**
+ * safe_fclose:
+ * @f: A FILE* stream, must have underlying fd
+ *
+ * Unix defaults for data preservation after system crash
+ * are unspecified, and many systems will eat your data
+ * in this situation unless you explicitly fsync().
+ *
+ * Returns: %TRUE on success, %FALSE on failure, and will set errno()
+ */
+static gboolean
+safe_fclose (FILE *f)
+{
+  int fd = fileno (f);
+  g_assert (fd >= 0);
+  if (fflush (f) == EOF)
+    return FALSE;
+  if (fsync (fd) < 0)
+    return FALSE;
+  if (fclose (f) == EOF)
+    return FALSE;
+  return TRUE;
+}
+
 static void
 build_cache (const gchar *path)
 {
@@ -1432,7 +1456,6 @@ build_cache (const gchar *path)
   gchar *bak_cache_path = NULL;
 #endif
   GHashTable *files;
-  gboolean retval;
   FILE *cache;
   struct stat path_stat, cache_stat;
   struct utimbuf utime_buf;
@@ -1490,17 +1513,22 @@ opentmp:
     }
     
   /* FIXME: Handle failure */
-  retval = write_file (cache, files, directories);
-  fclose (cache);
+  if (!write_file (cache, files, directories))
+    {
+      g_unlink (tmp_cache_path);
+      exit (1);
+    }
 
-  g_list_foreach (directories, (GFunc)g_free, NULL);
-  g_list_free (directories);
-  
-  if (!retval)
+  if (!safe_fclose (cache))
     {
+      g_printerr (_("Failed to write cache file: %s\n"), g_strerror (errno));
       g_unlink (tmp_cache_path);
       exit (1);
     }
+  cache = NULL;
+
+  g_list_foreach (directories, (GFunc)g_free, NULL);
+  g_list_free (directories);
 
   if (!validate_file (tmp_cache_path))
     {